package com.ibm.moa.outmail.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

public class RFC2047Decoder {
	private String source;
	private String pooledCES;
	private byte[] pooledBytes;
	private StringBuffer buf;
	private int pos = 0;
	private int startIndex;
	private int endIndex;

	public RFC2047Decoder(String source) {
		this.source = source;
		buf = new StringBuffer(source.length());
		parse();
	}

	public static String unfold(String source) {
		if (source == null)
			return null;
		StringBuffer buf = new StringBuffer();
		boolean skip = false;
		char c;
		// <CRLF>僔乕働儞僗傪慜採偲偡傞側傜indexOf()偱廫暘偱偡偑丄
		// 擮偺偨傔CR丄LF偄偢傟傕嫋梕偟傑偡丅
		for (int i = 0; i < source.length(); i++) {
			c = source.charAt(i);
			if (skip) {
				if (isLWSP(c)) {
					continue;
				}
				skip = false;
			}
			if (c != '\r' && c != '\n') {
				buf.append(c);
			} else {
				buf.append(' ');
				skip = true;
			}
		}
		return new String(buf);
	}

	public static boolean isLWSP(char c) {
		return c == '\r' || c == '\n' || c == ' ' || c == '\t';
	}

	public int indexOfNonLWSP(String source, int startIndex, boolean decrease) {
		char c;
		int inc = 1;
		if (decrease)
			inc = -1;
		for (int i = startIndex; i >= 0 && i < source.length(); i += inc) {
			c = source.charAt(i);
			if (!isLWSP(c)) {
				return i;
			}
		}
		return -1;
	}

	private void parse() {
		while (hasEncodedWord()) {
			String work = source.substring(pos, startIndex);
			if (indexOfNonLWSP(work, 0, false) > -1) {
				sweepPooledBytes();
				buf.append(work);
			} // encoded-word摨巑偺娫偺LWSP偼嶍彍
			parseWord();
		}
		sweepPooledBytes();
		buf.append(source.substring(pos));
	}

	// encoded-word 偑偁偭偨応崌丄startIndex/endIndex 傪僙僢僩偡傞
	private boolean hasEncodedWord() {
		startIndex = source.indexOf("=?", pos);
		if (startIndex == -1)
			return false;
		endIndex = source.indexOf("?=", startIndex + 2);
		if (endIndex == -1)
			return false;
		// 杮棃偼 encoded-word 拞偵 LWSP 偑偁偭偰偼偄偗側偄偑
		// encoded-word 偺搑拞偱 folding 偟偰偟傑偆 sender 偑偄傞傜偟偄
		// 埲壓傪僐儊儞僩偵偡傞偙偲偱 encoded-word 偺岆擣幆偺壜擻惈傕
		// 弌偰偔傞偑丄岆擣幆偵側傞妋棪埲忋偵慜婰偺傛偆側 illegal 側
		// 儊僢僙乕僕偺曽偑懡偄偺偑幚忣偺傛偆偩丅
		// thx > YOSI
		// int i = indexOfLWSP(source, startIndex + 2, false, (char)0);
		// if (i >= 0 && i < endIndex)
		// return false;
		endIndex += 2;
		return true;
	}

	public int indexOfLWSP(String source, int startIndex, boolean decrease,
			char additionalDelimiter) {
		char c;
		int inc = 1;
		if (decrease)
			inc = -1;
		for (int i = startIndex; i >= 0 && i < source.length(); i += inc) {
			c = source.charAt(i);
			if (isLWSP(c) || c == additionalDelimiter) {
				return i;
			}
		}
		return -1;
	}

	private byte[] decodeByTES(String s, String tes) {
		// 捠忢偁傝摼側偄偑丄LWSP 傪媗傔傞
		int i;
		while ((i = indexOfLWSP(s, 0, false, (char) 0)) >= 0)
			s = s.substring(0, i) + s.substring(i + 1);
		if (tes.equalsIgnoreCase("B") && s.length() % 4 != 0) {
			// BASE64DecoderStream 偼惓妋偵僷僨傿儞僌偝傟偰偄側偄偲
			// IOException 偵側傞偺偱丄柍棟傗傝嫺惓丅
			switch (4 - s.length() % 4) {
			case 1:
				s += '=';
				break;
			case 2:
				s += "==";
				break;
			case 3:
				if (s.charAt(s.length() - 1) != '=')
					s += "===";
				else
					s = s.substring(0, s.length() - 1);
				break;
			}
		}
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(
					com.sun.mail.util.ASCIIUtility.getBytes(s));
			InputStream is;
			if (tes.equalsIgnoreCase("B"))
				is = new com.sun.mail.util.BASE64DecoderStream(bis);
			else if (tes.equalsIgnoreCase("Q"))
				is = new com.sun.mail.util.QDecoderStream(bis);
			else
				throw new UnsupportedEncodingException(tes);
			int count = bis.available();
			byte[] bytes = new byte[count];
			count = is.read(bytes, 0, count);
			if (count != bytes.length) {
				byte[] w = new byte[count];
				System.arraycopy(bytes, 0, w, 0, count);
				bytes = w;
			}
			return bytes;
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException("CANT HAPPEN");
		}
	}

	private void parseWord() {
		try {
			int s = startIndex + 2;
			int e = source.indexOf('?', s);
			if (e == endIndex - 2)
				throw new RuntimeException();
			String ces = source.substring(s, e);
			try {
				"".getBytes(ces); // FIXME: check whether supported or not
			} catch (UnsupportedEncodingException ex) {
				ces = "gb2312";
			}
			s = e + 1;
			e = source.indexOf('?', s);
			if (e == endIndex - 2)
				throw new RuntimeException();
			String tes = source.substring(s, e);
			byte[] bytes = decodeByTES(source.substring(e + 1, endIndex - 2),
					tes);
			if (ces.equals(pooledCES)) {
				// append bytes
				byte[] w = new byte[pooledBytes.length + bytes.length];
				System.arraycopy(pooledBytes, 0, w, 0, pooledBytes.length);
				System.arraycopy(bytes, 0, w, pooledBytes.length, bytes.length);
				pooledBytes = w;
			} else {
				sweepPooledBytes();
				pooledCES = ces;
				pooledBytes = bytes;
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			// contains RuntimeException
			buf.append(source.substring(startIndex, endIndex));
		}
		pos = endIndex;
	}

	private void sweepPooledBytes() {
		if (pooledBytes == null)
			return;
		try {
			buf.append(new String(pooledBytes, pooledCES));
		} catch (UnsupportedEncodingException e) {
			throw new InternalError("CANT HAPPEN: Illegal encoding = "
					+ pooledCES);
		}
		pooledCES = null;
		pooledBytes = null;
	}

	public String get() {
		return new String(buf);
	}
}
